package day28.homework;

/**
 * 顾客类，负责购买包子
 * 实现Runnable接口，作为线程任务执行
 * 
 * HashMap：哈希表实现，无序
 * LinkedHashMap：保持插入顺序
 * TreeMap：按键的自然顺序或自定义顺序排序
 * 
 * 这个类代表包子铺的顾客，每个顾客只能买一个包子
 * 顾客有偏好的包子口味，会尝试购买该口味的包子
 * 
 * 多线程相关知识点：
 * 1. Runnable接口：实现多线程的一种方式，相比继承Thread类更灵活
 * 2. 线程命名：通过Thread构造方法为线程设置名称，便于调试和日志输出
 * 3. 线程等待：当资源不足时，线程进入等待状态直到资源可用
 * 4. 线程休眠：使用Thread.sleep()方法模拟实际操作的时间消耗
 * 5. 线程中断处理：正确处理InterruptedException异常
 */
public class Customer implements Runnable {
    /** 
     * 包子铺引用，用于调用购买包子的方法
     * 通过构造方法传入，实现对象间的依赖关系
     */
    private BunShop bunShop;
    
    /** 顾客姓名 */
    private String customerName;
    
    /** 顾客偏好的包子口味 */
    private Bun.Flavor preferredFlavor;
    
    /** 
     * 购买标志位，标记是否已经购买了包子
     * 使用volatile关键字确保多线程环境下的可见性
     */
    private volatile boolean hasBought = false;
    
    /**
     * 构造方法，初始化顾客信息
     * 通过构造方法注入依赖，符合面向对象设计原则
     * 
     * @param bunShop 包子铺对象引用
     * @param customerName 顾客姓名
     * @param preferredFlavor 偏好的包子口味
     */
    public Customer(BunShop bunShop, String customerName, Bun.Flavor preferredFlavor) {
        this.bunShop = bunShop;
        this.customerName = customerName;
        this.preferredFlavor = preferredFlavor;
    }
    
    /**
     * 线程执行方法，实现顾客的购买逻辑
     * 顾客来到包子铺，尝试购买偏好的包子口味
     * 如果包子售完，则会等待包子师傅制作
     * 购买完成后会模拟吃包子的过程
     * 
     * 线程执行流程详解：
     * 1. 线程启动后进入run方法开始执行
     * 2. 输出顾客来到包子铺的信息
     * 3. 调用包子铺的buyBun方法购买包子
     *    - 如果对应口味的包子有库存，直接购买
     *    - 如果对应口味的包子售完，线程会等待包子师傅制作
     *    - 购买完成后会唤醒等待的线程
     * 4. 模拟吃包子的过程，线程休眠2秒钟
     * 5. 输出吃包子完成的信息
     * 6. 线程正常结束或被中断时退出run方法
     * 
     * 线程等待机制详解：
     * 1. 当包子售完时，buyBun方法内部会调用wait()方法
     * 2. wait()方法使当前线程释放BunShop对象的锁并进入等待状态
     * 3. 线程进入BunShop对象的等待队列中
     * 4. 直到包子师傅调用notifyAll()方法才会被唤醒
     * 5. 被唤醒后需要重新获取锁才能继续执行
     */
    @Override
    public void run() {
        System.out.println("顾客: " + customerName + "来到包子铺，想要买" + preferredFlavor + "包子");
        
        try {
            // 购买包子，调用包子铺的buyBun方法
            // 这个方法内部使用了同步机制保证线程安全
            // 如果包子售完，当前线程会进入等待状态，直到包子师傅制作出新的包子
            Bun bun = bunShop.buyBun(preferredFlavor);
            // 设置购买标志位为true，表示已经购买了包子
            // volatile变量保证了多线程环境下的可见性
            hasBought = true;
            
            // 享用包子，模拟吃包子的过程
            System.out.println("顾客: " + customerName + "正在享用" + bun);
            // 使用Thread.sleep模拟吃包子需要时间
            // 线程会暂停执行2秒钟，模拟吃包子的过程
            Thread.sleep(2000); // 模拟吃包子的时间
            System.out.println("顾客: " + customerName + "吃完了" + bun);
            
        } catch (InterruptedException e) {
            // 当线程在sleep或wait期间被中断时，会抛出InterruptedException异常
            // 这种情况可能发生在程序被强制终止时
            System.out.println("顾客: " + customerName + "被中断了");
            // 重新设置线程的中断状态，保持线程的中断状态不变
            // 这是一种良好的编程实践，确保中断状态能正确传递
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * 获取购买状态
     * 使用volatile关键字确保多线程环境下的可见性
     * 
     * @return 是否已经购买了包子
     */
    public boolean hasBought() {
        return hasBought;
    }
    
    /**
     * 获取顾客姓名
     * 
     * @return 顾客姓名
     */
    public String getCustomerName() {
        return customerName;
    }
}